/**
 * @file unistd.h
 * @brief Äquivalenzbibliothek für PTK
 *
 * @author Michael Kammer
 * @author Julius Werner
 * @date 22.08.2011
*/
#ifndef __UNISTD_H_
#define __UNISTD_H_

#include "fcntl_pt.h"
#include "ptlib.h"
#include "sys/stat_pt.h"
#include "stddef.h"
#include "stdint.h"

#include "ptk_fs.h"
#include "ptk_types.h"
#include "sys/sysinfo.h"

#define max(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a > _b ? _a:_b; })
#define min(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a < _b ? _a:_b; })
#define swp32(x) ((typeof(x))({uint32_t _x=(uint32_t)(x); (_x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x00ff00) | (x >> 24);}))
#define swp16(x) ((typeof(x))({uint16_t _x=(uint16_t)(x); (_x << 8) | (_x >> 8);}))

/**
 * @brief Billige Implementation, bis jemand einen ordentlichen (blockierenden) Mikrosekunden-Sleep implementiert
 */
#define usleep(n) for(uint32_t _my_sleep_var = 0, _my_sleep_limit = n; _my_sleep_var < _my_sleep_limit; _my_sleep_var++) asm volatile ("MOV R0, R0")

/**
 * @brief
 *   Erzeugt eine Kopie des Prozesses
 *
 * Die Funktion erstellt eine Kopie des aufrufenden Prozesses, der sich
 * nur in seiner Prozess-ID unterscheidet.
 *
 * @return
 *   Prozess-ID des neuen Prozesses oder \c -1 im Fehlerfall. Im
 *   erzeugten Prozess ist der Rückgabewert \c 0.
 */
pid_t fork(void);

#define vfork fork

/**
 * @brief Beendet den Prozess, ohne atexit()-Handler aufzurufen. Schliesst alle Dateideskriptoren.
 */
void _exit(int status) __attribute__((noreturn));

/**
 * @brief
 *   Behandelt eine Speicheradresse als ELF-Datei und führt sie aus
 * 
 * Die Funktion lädt die durch den Pointer referenzierte ELF-Datei und
 * ersetzt den aktuellen Prozess durch dieses Programm. Weiterhin können
 * Kommandozeilen-Parameter, Umgebungsvariablen und Dateideskriptoren
 * übergeben werden.
 * 
 * @param ptr   : Pointer auf das ELF-File. Bei NULL wird das BusyBox
 *                Prozessimage aus der kernelinternen Ramdisk geladen.
 * @param *argv : zu übergebende Parameter, argv[0] sollte auf den 
 *               Dateinamen des Programms zeigen. Der letzte Eintrag des
 *               Arrays muss \c NULL sein.
 * @param *env  : NULL-terminiertes Array mit Umgebungsvariablen im
 *                NAME=VALUE Format fuer das neue Programm
 * @param *fs_state : Dateideskriptoren und Arbeitsverzeichnis fuer
 *                das neue Programm
 * 
 * @return
 *   im Fehlerfall -1, im Erfolgsfall kehrt sie offensichtlich nicht
 *   zurück
 */
static inline signed int pt_exec(void* ptr, char* const argv[], char* const env[], fs_state_t* fs_state)
{
	exec_param_t param = {ptr, argv, env, fs_state};
	return (signed int) pt_syscall1(oc_exec, &param);
}

int execl(const char *, ...);
int execle(const char *, ...);
//int execlp(const char *, const char *, ...);
int execv(const char *, char * const *);
int execve(const char *, char * const *, char * const *);
//int execvp(const char *, char * const []);
#define execlp execl
#define execvp execv
// TODO: execlp/execvp korrekt implementieren

/**
 * @brief
 *   Unterbricht die Prozessausführung für einige Sekunden
 *
 * Die Funktion hält die Prozessausführung für die angegebene Anzahl an
 * Sekunden an.
 *
 * @param seconds : Anzahl der zu wartenden Sekunden
 *
 * @return
 *   Funktion gibt immer Null zurück
 */
unsigned int pt_sleep(unsigned int seconds);
int pt_stat(const char* path, struct stat* buf);
/**
 * @brief
 *   Ermittelt die Prozess-ID
 *
 * @return
 *   Prozess-ID des aktuellen Prozesses
 */
static inline pid_t pt_getpid(void)
{
	return (pid_t) pt_syscall0(oc_getpid);
}

/**
 * @brief
 *   Ermittelt die Prozess-ID des Elternprozesses
 *
 * @return
 *   Prozess-ID des Elternprozesses des aufrufenden Prozesses
 */
static inline pid_t getppid(void)
{
	return (pid_t) pt_syscall0(oc_getppid);
}

/**
 * @brief Dummy-Implementation. Bei uns gibt es keine Terminals -> ENOTTY
 */
pid_t tcgetpgrp(int fildes);

/**
 * @brief Dummy-Implementation. Bei uns gibt es keine Terminals -> ENOTTY
 */
int tcsetpgrp(int fildes, pid_t pgid_id);

#define _SC_ARG_MAX 1
#define _SC_CLK_TCK 3
#define _SC_PAGESIZE 29

long sysconf(int name);

gid_t getegid(void);
uid_t geteuid(void);
gid_t getgid(void);
uid_t getuid(void);
int getgroups(int, gid_t []);
int setgid(gid_t);
int setuid(uid_t);
int setregid(gid_t, gid_t);
int setreuid(uid_t, uid_t);
int setegid(gid_t);
int seteuid(uid_t);

#ifndef _GETOPT_DEFINED_
#define _GETOPT_DEFINED_
int	 getopt(int, char * const *, const char *);
int	 getsubopt(char **, char * const *, char **);

extern   char *optarg;                  /* getopt(3) external variables */
extern   int opterr;
extern   int optind;
extern   int optopt;
extern   int optreset;
extern   char *suboptarg;               /* getsubopt(3) external variable */
#endif

#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

#define F_OK 0
#define R_OK 4
#define W_OK 2
#define X_OK 1

/**
 * @brief Dummy-Implementation. Gibt immer 0 zurueck.
 */
int pt_isatty(int fd);

/**
 * @brief
 *   Öffnet einen Kanal zum (nicht-)blockierenden
 *   Lesen und/oder schreiben als Dateideskriptor.
 *   Nicht thread-safe!
 */
int copen(int channel_id, int oflag);

/**
 * @brief
 *   POSIX-kompatibles read(). Nicht thread-safe!
 */
ssize_t pt_read(int fd, void* buf, size_t len);

/**
 * @brief
 *   POSIX-kompatibles write(). Nicht thread-safe!
 */
ssize_t pt_write(int fd, const void* buf, size_t len);

/**
 * @brief
 *   POSIX-kompatibles lseek(). Nicht thread-safe!
 */
ssize_t pt_lseek(int fd, size_t offset, int whence);

/**
 * @brief
 *   POSIX-kompatibles close(). Nicht thread-safe!
 */
int pt_close(int fd);

/**
 * @brief
 *   Dummy-Implementation. Schlägt immer fehl.
 */
int link(const char *old, const char *new);

/**
 * @brief
 *   Dummy-Implementation. Schlägt immer fehl.
 */
int unlink(const char* name);


/**
 * @brief
 *   POSIX-kompatibles pipe(). Nicht thread-safe!
 *   Unterstützt im Moment weder EPIPE beim sterben
 *   des anderen Prozesses, noch blockieren bei Überlauf.
 */
int pipe(int fildes[2]);

/**
 * @brief
 *   POSIX-kompatibles dup(). Nicht thread-safe!
 */
int dup(int fd);

/**
 * @brief
 *   POSIX-kompatibles dup2(). Nicht thread-safe!
 */
int dup2(int fd, int new_fd);

/**
 * @brief
 *   POSIX-kompatibles chdir(). Nicht thread-safe!
 */
int chdir(const char* path);

/**
 * @brief
 *   POSIX-kompatibles getcwd(). Nicht thread-safe!
 */
char* getcwd(char* buf, size_t size);

/**
 * @brief
 *   POSIX-kompatibles access(). Nicht thread-safe!
 */
int access(const char* path, int amode);

#endif
